package com.bdv.md.en;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.util.List;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.log4j.Logger;
import org.w3c.dom.Node;
import org.w3c.dom.bootstrap.DOMImplementationRegistry;
import org.w3c.dom.ls.DOMImplementationLS;
import org.w3c.dom.ls.LSSerializer;
import org.xml.sax.InputSource;

import com.bdv.md.controller.xmlattribute.Parameter;
import com.bdv.md.controller.xmlattribute.TlkmTsel;
import com.bdv.md.controller.xmlattribute.TselTlkm;

public enum RequestEn {
	CC_AMR,
	CC_TOKEN_REQUEST,
	CC_LIM_KREDIT,
	CC_HISTORY_TOKEN,
	CC_BILLING_HISTORY,
	
	CC_AMR_PP,
	CC_LOAD_PROFILE_PP,
	CC_EVENT_LOG_PP,
	CC_BILLING_PP,
	CC_RECALL_LOAD_PROFILE_PP,
	CC_TIME_SYNC_PP,
	CC_REMOTE_PP,
	CC_SCADA_SYSTEM_PP
	;
	
	Logger logger = Logger.getLogger(this.getClass());
	private RequestEn(){}
	
	public TselTlkm request(Parameter parameter){
		TlkmTsel tlkmTsel = null;
		String url = null;
		String xml = null;
		String responseXml = null;
		TselTlkm response = null;
		
		switch (this) {
		case CC_AMR: 
			System.out.println("REFIRE AMR "+parameter.getTrxId());
			tlkmTsel = new TlkmTsel(HTTPValues.TYPE_SUBMIT_READ_METER.getName(), parameter);
			url = UrlList.USSD_AMR_REQUEST.getUrl();
			break;
		case CC_TOKEN_REQUEST:
			System.out.println("REFIRE TOKEN REQ "+parameter.getTrxId());
			tlkmTsel = new TlkmTsel(HTTPValues.TYPE_SUBMIT_TOKEN.getName(), parameter);
			url = UrlList.USSD_TOKEN_REQUEST.getUrl();
			break;
		case CC_LIM_KREDIT:
			System.out.println("REFIRE LIMIT KREDIT "+parameter.getTrxId());
			tlkmTsel = new TlkmTsel(HTTPValues.TYPE_SUBMIT_SET_CL.getName(), parameter);
			url = UrlList.USSD_LIMKREDIT_REQUEST.getUrl();	
			break;
		case CC_HISTORY_TOKEN: 
			System.out.println("REFIRE HISTORY TOKEN "+parameter.getTrxId());
			tlkmTsel = new TlkmTsel(HTTPValues.TYPE_SUBMIT_TOKEN_HIST.getName(), parameter);
			url = UrlList.USSD_HISTOKEN_REQUEST.getUrl();
			break;
		case CC_BILLING_HISTORY: 
			System.out.println("REFIRE BILLING HISTORY "+parameter.getTrxId());
			tlkmTsel = new TlkmTsel(HTTPValues.TYPE_SUBMIT_BILLING_HIST.getName(), parameter);
			url = UrlList.USSD_BILHISTORY_REQUEST.getUrl();
			break;
		case CC_AMR_PP: 
			System.out.println("REFIRE AMR PP "+parameter.getTrxId());
			tlkmTsel = new TlkmTsel(HTTPValues.TYPE_SUBMIT_METER_READING_PP.getName(), parameter);
			url = UrlList.USSD_AMR_PP_REQUEST.getUrl();
			break;
		case CC_LOAD_PROFILE_PP: 
			System.out.println("REFIRE LOAD PROFILE PP "+parameter.getTrxId());
			tlkmTsel = new TlkmTsel(HTTPValues.TYPE_SUBMIT_LOAD_PROFILE_PP.getName(), parameter);
			url = UrlList.USSD_LOAD_PROFILE_PP_REQUEST.getUrl();
			break;
		case CC_EVENT_LOG_PP: 
			System.out.println("REFIRE EVENT_LOG PP "+parameter.getTrxId());
			tlkmTsel = new TlkmTsel(HTTPValues.TYPE_SUBMIT_EVENT_LOG_PP.getName(), parameter);
			url = UrlList.USSD_EVENT_LOG_PP_REQUEST.getUrl();
			break;
		case CC_BILLING_PP: 
			System.out.println("REFIRE BILLING PP "+parameter.getTrxId());
			tlkmTsel = new TlkmTsel(HTTPValues.TYPE_SUBMIT_BILLING_PP.getName(), parameter);
			url = UrlList.USSD_BILLING_PP_REQUEST.getUrl();
			break;
		case CC_RECALL_LOAD_PROFILE_PP: 
			System.out.println("REFIRE RECALL LOAD PROFILE PP "+parameter.getTrxId());
			tlkmTsel = new TlkmTsel(HTTPValues.TYPE_SUBMIT_RECALL_LOAD_PROFILE_PP.getName(), parameter);
			url = UrlList.USSD_RECALL_LOAD_PROFILE_PP_REQUEST.getUrl();
			break;
		case CC_TIME_SYNC_PP: 
			System.out.println("REFIRE TIME_SYNC PP "+parameter.getTrxId());
			tlkmTsel = new TlkmTsel(HTTPValues.TYPE_SUBMIT_TIME_SYNC_PP.getName(), parameter);
			url = UrlList.USSD_TIME_SYNC_PP_REQUEST.getUrl();
			break;
		case CC_REMOTE_PP: 
			System.out.println("REFIRE REMOTE PP "+parameter.getTrxId());
			tlkmTsel = new TlkmTsel(HTTPValues.TYPE_SUBMIT_REMOTE_PP.getName(), parameter);
			url = UrlList.USSD_REMOTE_PP_REQUEST.getUrl();
			break;
		case CC_SCADA_SYSTEM_PP: 
			System.out.println("REFIRE SCADA_SYSTEM PP "+parameter.getTrxId());
			tlkmTsel = new TlkmTsel(HTTPValues.TYPE_SUBMIT_SCADA_SYSTEM_PP.getName(), parameter);
			url = UrlList.USSD_SCADA_SYSTEM_PP_REQUEST.getUrl();
			break;
		default:
		}
		
		xml = parseTlkmTselToXML(tlkmTsel);
		responseXml = httpRequestPost(url, xml);
		response = parseXMLtoTselTlkm(responseXml);
		System.out.println("RESPONSE OF REFIRE "+parameter.getTrxId()+" :\n"+formatXML(responseXml));
		return response;
	}
	
	private String parseTlkmTselToXML(TlkmTsel tlkmTsel) {
		String xmlResult = null;
		
		System.out.println("Parsing Object TlkmTsel : "+tlkmTsel);

		StreamResult xmlStreamResult = new StreamResult(new ByteArrayOutputStream());

		try {
			JAXBContext jaxbContext = JAXBContext
					.newInstance(TlkmTsel.class);
			Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
            
			jaxbMarshaller.marshal(tlkmTsel, xmlStreamResult);

			xmlResult = xmlStreamResult.getOutputStream().toString();
			return xmlResult;

		} catch (JAXBException e) {
			logger.error("Parse TlkmTsel to XML Failed TlkmTsel:"+tlkmTsel);
			e.printStackTrace();
			return null;
		}

	}
	
	private String parseTselTlkmToXML(TselTlkm tselTlkm) {
		String xmlResult = null;
		
		System.out.println("Parsing Object TlkmTsel : "+tselTlkm);

		StreamResult xmlStreamResult = new StreamResult(new ByteArrayOutputStream());

		try {
			JAXBContext jaxbContext = JAXBContext
					.newInstance(TselTlkm.class);
			Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
            
			jaxbMarshaller.marshal(tselTlkm, xmlStreamResult);

			xmlResult = xmlStreamResult.getOutputStream().toString();
			return xmlResult;

		} catch (JAXBException e) {
			logger.error("Parse Tseltlkm to XML Failed Tseltlkm:"+tselTlkm);
			e.printStackTrace();
			return null;
		}

	}
	/**
	 * HTTP REQUEST POST METHOD
	 * this will send a request call to the url parameter with the xml as body
	 */
	private String httpRequestPost(String url, String xml){
		System.out.println("POST TO "+url+" XML : \n"+formatXML(xml));
		PostMethod post = new PostMethod(url);   
		String response = null;
	    try {
	    	
	        StringRequestEntity requestEntity = new StringRequestEntity(xml);
	        post.setRequestEntity(requestEntity);
	        
	        post.setRequestEntity(requestEntity);
	        post.setRequestHeader("Content-type",  "text/xml; charset=UTF-8");
	        org.apache.commons.httpclient.HttpClient httpclient = new org.apache.commons.httpclient.HttpClient();
	        int result = httpclient.executeMethod(post);
	        
	        response = post.getResponseBodyAsString();
	        
	    } catch (IOException e) {
	        logger.error(e.getMessage());
	        return null;
	    } finally {
	        post.releaseConnection();
	    }
	    return response;
	}

	public String formatXML(String xml) {

		try {
			final InputSource src = new InputSource(new StringReader(xml));
			final Node document = DocumentBuilderFactory.newInstance()
					.newDocumentBuilder().parse(src).getDocumentElement();
			final Boolean keepDeclaration = Boolean.valueOf(xml
					.startsWith("<?xml"));

			// May need this:
			// System.setProperty(DOMImplementationRegistry.PROPERTY,"com.sun.org.apache.xerces.internal.dom.DOMImplementationSourceImpl");

			final DOMImplementationRegistry registry = DOMImplementationRegistry
					.newInstance();
			final DOMImplementationLS impl = (DOMImplementationLS) registry
					.getDOMImplementation("LS");
			final LSSerializer writer = impl.createLSSerializer();

			writer.getDomConfig().setParameter("format-pretty-print",
					Boolean.TRUE); // Set this to true if the output needs to be
									// beautified.
			writer.getDomConfig().setParameter("xml-declaration",
					keepDeclaration); // Set this to true if the declaration is
										// needed to be outputted.

			return writer.writeToString(document);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	private TselTlkm parseXMLtoTselTlkm(String xml){
		
		try {
			JAXBContext jc = JAXBContext.newInstance(TselTlkm.class);
	        Unmarshaller unmarshaller = jc.createUnmarshaller();
	        StreamSource xmlSource = new StreamSource(new StringReader(xml));
	        TselTlkm tsel = (TselTlkm)unmarshaller.unmarshal(xmlSource);
			return tsel;
		} catch (JAXBException e) {
			logger.error("Parse XML to TselTlkm Failed XML:\n"+formatXML(xml));
			logger.error(e.getMessage());
			return null;
		}
	}
	
}
